/*
 * Copyright 2013 Sigurd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.digiway.rapidbreeze.client.config;

import de.digiway.rapidbreeze.client.infrastructure.BusEvents;
import de.digiway.rapidbreeze.client.infrastructure.ThreadPoolService;
import de.digiway.rapidbreeze.client.model.config.ServerConfigurationModel;
import de.digiway.rapidbreeze.client.model.config.ServerConfigurationModelList;
import de.digiway.rapidbreeze.client.model.config.ServerConfigurationResourceRepository;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import org.rribbit.RRB;

/**
 * This class monitors all changes in the server configuration and fires an
 * event is a change occurs.
 *
 * @author Sigurd
 */
public class ServerConfigurationListener {

    private ScheduledExecutorService executorService;
    private ServerConfigurationResourceRepository configurationRepository;
    private ServerConfigurationModelList list;
    private boolean enabled = false;
    private ListenerSupport listenerSupport;
    private Map<String, PropertyChangeListener> propertyChangeListeners;
    private static final int POLLING_INTERVAL_MS = 500;
    private static final Logger LOG = Logger.getLogger(ServerConfigurationListener.class.getName());

    ServerConfigurationListener(ServerConfigurationResourceRepository configurationRepository) {
        this.executorService = ThreadPoolService.getScheduledPool();
        this.configurationRepository = configurationRepository;
        this.list = new ServerConfigurationModelList();
        this.propertyChangeListeners = new HashMap<>();
        this.listenerSupport = new ListenerSupport();

        this.executorService.scheduleAtFixedRate(new CheckConfiguration(), 0, POLLING_INTERVAL_MS, TimeUnit.MILLISECONDS);
    }

    public void startMonitoring() {
        list.addListener(listenerSupport);
        enabled = true;
    }

    public void stopMonitoring() {
        enabled = false;
        list.removeListener(listenerSupport);
    }

    private class ListenerSupport implements ListChangeListener<ServerConfigurationModel> {

        @Override
        public void onChanged(Change<? extends ServerConfigurationModel> change) {
            while (change.next()) {
                if (change.wasAdded()) {
                    for (ServerConfigurationModel model : change.getAddedSubList()) {
                        PropertyChangeListener changeListener = new PropertyChangeListener(model.getKey());
                        propertyChangeListeners.put(model.getKey(), changeListener);
                        model.valueProperty().addListener(changeListener);
                        // First first time configuration is read:
                        changeListener.fireEvent(null, model.getValue());
                    }
                } else if (change.wasRemoved()) {
                    for (ServerConfigurationModel model : change.getRemoved()) {
                        model.valueProperty().removeListener(propertyChangeListeners.remove(model.getKey()));
                    }
                }
            }
        }
    }

    private static class PropertyChangeListener implements ChangeListener<Object> {

        private String key;

        public PropertyChangeListener(String key) {
            this.key = key;
        }

        @Override
        public void changed(ObservableValue<? extends Object> ov, Object oldValue, Object newValue) {
            fireEvent(oldValue, newValue);
        }

        public void fireEvent(Object oldValue, Object newValue) {
            ServerConfigurationEvent event = new ServerConfigurationEvent(this, key, oldValue, newValue);
            RRB.get().send(BusEvents.SERVER_CONFIGURATION_CHANGED_EVENT, event);
        }
    }

    private class CheckConfiguration implements Runnable {

        @Override
        public void run() {
            if (enabled) {
                try {
                    list.update(configurationRepository.getEntries());
                } catch (RuntimeException ex) {
                    LOG.log(Level.SEVERE, "An exception occured during configuration check.", ex);
                }
            }
        }
    }
}
